package org.zoomdev.zoom.dao.impl;

import org.apache.commons.lang3.StringUtils;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.expression.Symbol;
import org.zoomdev.zoom.dao.ExpressionMeta;
import org.zoomdev.zoom.dao.Select;
import org.zoomdev.zoom.dao.Where;
import org.zoomdev.zoom.dao.structs.JoinMeta;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

@SuppressWarnings("ALL")
public class SelectImpl implements Select {
    static final String AND = " AND ";
    static final String OR = " OR ";
    List<JoinMeta> tables = new ArrayList<>();
    List<Object> conditions = new ArrayList<>();
    List<OrderMeta> orders = new ArrayList<>();
    List<StatisticsMeta> statisticsMetas = new ArrayList<>();
    List<Object> values = new ArrayList<>();
    int limit;
    int position;
    String table;
    List<String> group = new ArrayList<>();

    List<Object> having = new ArrayList<>();

    public SelectImpl() {
    }

    public Select from(String table) {
        this.table = table;
        return this;
    }

    public Select limit(int limit) {
        return limit(0, limit);
    }

    public Select limit(int position, int limit) {
        this.position = position;
        this.limit = limit;
        return this;
    }

    @Override
    public Select like(String key, Like like, Object value) {
        return relationLike(key, like, value, AND, false);
    }

    private Select relationLike(String key, Like like, Object value, String relation, boolean not) {
        assert (key != null);
        conditions.add(new WhereMeta(key, " LIKE ", relation, "?", not));
        values.add(like.toValue(value));
        return this;
    }

    @Override
    public Select orLike(String key, Like like, Object value) {
        return relationLike(key, like, value, OR, false);
    }

    @Override
    public Select where(String key, Symbol symbol, Object value) {
        assert (key != null && symbol != null);
        return whereImpl(key, symbol, value, AND);
    }

    protected Select whereImpl(String key, Symbol symbol, Object value, String relation) {
        conditions.add(new WhereMeta(key, symbol.value(), relation, "?", false));
        values.add(value);
        return this;
    }

    @Override
    public Select where(String key, Object value) {
        return whereImpl(key, Symbol.EQ, value, AND);
    }

    @Override
    public Select orWhere(String key, Object value) {
        return orWhere(key, Symbol.EQ, value);
    }

    public Select orWhere(String key, Symbol symbol, Object value) {
        return whereImpl(key, symbol, value, OR);
    }

    @Override
    public Select whereNull(String key) {
        return whereNull(key, AND, false);
    }

    private Select whereNull(String key, String relation, boolean not) {
        conditions.add(new WhereMeta(key, " IS ", relation, "NULL", not));
        return this;
    }


    @Override
    public Select whereNotNull(String key) {
        return whereNull(key, AND, true);
    }

    public Select whereNotIn(String key, Collection values) {
        return whereIn(key, AND, true, values);
    }

    public Select whereIn(String key, Collection values) {
        return whereIn(key, AND, false, values);
    }

    protected Select whereIn(String key, String relation, boolean not, Collection values) {
        conditions.add(new WhereMeta(key, " IN ", relation, StringUtils.repeat("?", ",", values.size()), not));
        values.addAll(values);
        return this;
    }

    public Select join(String otherTable, String on) {
        return join(otherTable, on, JoinType.INNER);
    }

    public Select join(String table, String on, JoinType type) {
        tables.add(new JoinMeta(table, on, type));
        return this;
    }

    @Override
    public Select orderBy(String key, Sort sort) {
        assert (sort != null && key != null);
        orders.add(new OrderMeta(key, sort));
        return this;
    }

    @Override
    public Select groupBy(String group) {
        this.group.add(group);
        return this;
    }

    public Select select(String fields) {
        for (String select : fields.split(",")) {
            parseSelect(select);
        }
        return this;
    }

    public Select select(Iterable<String> fields) {
        for (String select : fields) {
            parseSelect(select);
        }
        return this;
    }

    protected void parseSelect(String select) {

    }

    public Select clear() {
        clear(true);
        return this;
    }

    public void clear(boolean all) {
        if (all) {
            group = null;
            table = null;
            values.clear();
            tables.clear();
        }

        orders.clear();
    }

    // where a+b>c+? 之类的
    public Select whereExpression(String expression, Object... values) {
        return expression(expression, AND, values);
    }

    public Select orExpression(String expression, Object... values) {
        return expression(expression, OR, values);
    }

    protected Select expression(String expression, String relation, Object[] values) {
        conditions.add(new ExpressionMeta(expression, relation, values));
        return this;
    }

    void conditionWhere(String relation, Where<?> where, Condition condition) {
        conditions.add(new StartMeta(relation));
        condition.where(where);
        conditions.add(")");
    }

    @Override
    public Select where(Condition condition) {
        return where(this, condition);
    }

    @Override
    public Select orWhere(Condition condition) {
        return orWhere(this, condition);
    }

    public Select where(Where<?> sql, Condition condition) {
        conditionWhere(AND, sql, condition);
        return this;
    }

    public Select orWhere(Where<?> sql, Condition condition) {
        conditionWhere(OR, this, condition);
        return this;
    }

    @Override
    public Select having(String key, Symbol symbol, Object value) {
        if (group.size() == 0) {
            throw new ZoomException("本查询不包含group by,无法使用having");
        }
        this.having.add(new WhereMeta(key, symbol.value(), AND, "?", false));
        this.values.add(value);
        return this;
    }

    public Select notLike(String key, Like like, Object value) {
        return relationLike(key, like, value, AND, false);
    }

    protected Select selectFunc(String key, String alias, String function) {
        statisticsMetas.add(new StatisticsMeta(key, alias, function));
        return this;
    }


    public Select selectMax(String key, String alias) {
        return selectFunc(key, alias, "MAX");
    }

    public Select selectCount(String alias) {
        selectFunc("*", alias, "COUNT");
        return this;
    }

    public Select selectSum(String key, String alias) {
        return selectFunc(key, alias, "SUM");
    }


    public Select selectMin(String key, String alias) {
        return selectFunc(key, alias, "MIN");
    }


    public Select selectAvg(String key, String alias) {
        return selectFunc(key, alias, "AVG");
    }


}
